/*
 * File: ContactXMLParser.java
 * Written by Kyle McCready, Viet Le, and David Hudson
 * Started April 11, 2012
 * Stored at http://code.google.com/p/sa-con-man/ on Google code
 * 
 * This program was written as the final project for CSE489 - Zero Defect 
 * Software Design Spring 2012. It follows the specification submitted on 
 * April 11 2012, which can be viewed online at: http://goo.gl/MH0Ol
 * 
 * This file is the XML parser portion of the application, which works in 
 * conjunction with the following files:
 * ContactManager.java
 * ContactFileEncryptor.java
 * ContactXMLWriter.java
 */
package contactmanager;

import java.io.*;
import java.util.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


class ContactXMLError extends Exception
{
    public ContactXMLError()
    {
        super("Error Finding XML Element");
    }

    public ContactXMLError(String ElementName)
    {
        super("Error Finding XML Element " + ElementName);
    }
}


public class ContactXMLParser
{
    private String fileName;
    private Document dom;

    /* - - - C O N T A C T X M L P A R S E R
     *
     *  fileName = _fileName
     */
    public ContactXMLParser(String _fileName)
    {
        fileName = _fileName;
    }


    /* - - - I M P O R T C O N T A C T S
     * let
     *      \XML\ = byte array received when file pointed to by fileName
     *              is decrypted with password.
     * in ->
     *  if password is the key to decrypt file pointed to by fileName->
     *      contactList := contactList populated with contact given in
     *                      \XML\
     *  else ->
     *      System.err +:=  "XML file is not proper form,
     *                       likely, password is incorrect"
     */
    public void ImportContacts(ArrayList<ContactObject> contactList,
                               String password)
    {
        try
        {   
            /* - - - 1
             * dbf := new instace of DocumentBuilderFactory
             */
            DocumentBuilderFactory dbf = 
                DocumentBuilderFactory.newInstance();

            /* - - - 2
             * db := new DocumentBuilder from dbf
             */
            DocumentBuilder db = 
                dbf.newDocumentBuilder();
            
            /* - - - 3
             * cfd := new ContactFieDecryptor with key password
             */
            ContactFileDecryptor cfd = 
                new ContactFileDecryptor(password);

            /* - - - 4
             * plainxml := byte array of the /File/ decrypted with 
             *            password
             */
            byte[] plainxml = cfd.Decrypt(fileName);

            /* - - - 5
             * bais := ByteArrayInputStream with the plainxml in the 
             *          buffer
             */
            ByteArrayInputStream bais = 
                new ByteArrayInputStream(plainxml);
            
            /* - - - 6
             * if plainxml is in correct xml form ->
             *  dom := Document after parsing plainxml as an xml document
             *  contactList := contactList populated with contacts whose
             *                  information is plainxml
             * else ->
             *  throw ParserConfigurationException
             */
            dom = db.parse(bais);
            addContactsFromXML(contactList); 
        }
        catch(ContactXMLError e)
        {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
        catch(ParserConfigurationException e)
        {
            System.err.println("XML file is not proper form." +
                               "Likely, password is incorrect");
            e.printStackTrace();
        }
        catch(SAXException e)
        {
            System.err.println("Error Parsing XML file");
            e.printStackTrace();
        }
        catch(IOException e)
        {
            System.err.println("Error opening XML file");
            e.printStackTrace();
        }
    }

    /* - - - a d d C o n t a c t s F r o m X M L
     * if dom contains elements with the tag name ContactID ->
     *  contactList := ContactList populated with contacts whose information
     *                  is contained in the xml document repepresented by dom
     * else ->
     *  throw ContactXMLError
     */
    private void addContactsFromXML(ArrayList<ContactObject> contactList) 
        throws ContactXMLError
    {
        /* - - - 1
         * docEle := the root element of dom
         * nl := a NodeList of XML nodes with the tag name ContactID
         */
        Element docEle = dom.getDocumentElement();
        NodeList nl = docEle.getElementsByTagName("ContactID");

        /* - - - 2
         * if nl is empty ->
         *  throw ContactXMLError
         * else ->
         *  contactList := ContactList populated with contacts whose 
         *                  information is contained in the xml document 
         *                  repepresented by dom
         */
        if (nl == null || nl.getLength() == 0)
            /* - - 2.1
             */
            throw new ContactXMLError("ContactID");
        else
            /* - - 2.2
             * contactList := ContactList populated with contacts whose 
             *                  information is contained in the xml document 
             *                  repepresented by dom
             */
            for(int i = 0; i < nl.getLength(); i++)
            {
                /* - - 2.2 (body)
                 * let
                 *  /contact/ = contact represented by the ith (top down) element
                 *              with the tagname ContactID
                 * in ->
                 *  contactList := contactList with the /contact/ added to the end
                 */
                 
                 /* - 2.2.1
                  * el := The ith element (top down) with the tag name "ContactID"
                  */
                Element el = (Element)nl.item(i);

                /* - 2.2.2
                 * contact := ContactObject whose information is represented by
                 *              the element el
                 */
                ContactObject contact = getContactFromElement(el);

                /* 2.2.3
                 * contactList := contactList with contact added to the end
                 */
                contactList.add(contact);
            }
    }
    /* - - - g e t C o n t a c t F r o m E l e m e n t
     * let
     *  /ProperContactID/ = an XML element with the tag name ContactID
     *                      and with child elements with tag names ContactName,
     *                      ContactPhone, ContactAddress, Email, ContactIM
     * in ->
     *  if el is a /ProperContactID/ ->
     *      return a ContactObject whose information is represented by el
     *  else ->
     *      throw ContactXMLError
     */
    private ContactObject getContactFromElement(Element el) 
        throws ContactXMLError
    {
        /* - - - 1
         * contact := new instance of ContactObject
         */
        ContactObject contact = new ContactObject();

        /* - - - 2
         * let
         *  /ProperNameElement/ = an XML element with the tag name 
         *                          ContactName and with child elements 
         *                          with tag names FirstName, MiddleName,
         *                          SirName, and NickName
         * in ->
         *  if el has a /ProperNameElement/ child ->
         *      contact.name := contact.name with an added NameObject 
         *                      represented by the /ProperNameElement/ 
         *                      child of el
         *  else ->
         *      throw a ContactXMLError
         */
        addNamesFromElement(contact, el);

        /* - - - 3
         * let
         *  /PropePhoneElement/ = an XML element with the tag name 
         *                          ContactPhone and with child elements 
         *                          with tag names AreaCode and LocalNumber
         * in ->
         *  if el has a /ProperPhoneElement/ child ->
         *      contact.phoneNumber := contact.phoneNumber with an added PhoneObject 
         *                      represented by the /ProperPhoneElement/ 
         *                      child of el
         *  else ->
         *      throw a ContactXMLError
         */
        addPhonesFromElement(contact, el);

         /* - - - 4
         * let
         *  /ProperAddressElement/ = an XML element with the tag name 
         *                          ContactAddress and with child elements 
         *                          with tag names State, City,
         *                          Zip, and StreetAddress
         * in ->
         *  if el has a /ProperAddressElement/ ->
         *      contact.address := contact.address with an added AddressObject 
         *                      represented by the /ProperAddressElement/ 
         *                      child of el
         *  else ->
         *      throw a ContactXMLError
         */
        addAddressesFromElement(contact, el);

        /* - - - 5
         * let
         *  /ProperEmailElement/ = an XML element with the tag name 
         *                          ContactEmail
         * in ->
         *  if el has a /ProperEmailElement/ child ->
         *      contact.email := contact.email with an added EmailObject 
         *                      represented by the /ProperEmailElement/ 
         *                      child of el
         *  else ->
         *      throw a ContactXMLError
         */
        addEmailsFromElement(contact, el);

        /* - - - 6
         * let
         *  /ProperIMElement/ = an XML element with the tag name 
         *                      ContactIM and with child elements 
         *                      with tag names Provider and UserName
         * in ->
         *  if el has a /ProperIMElement/ child->
         *      contact.im := contact.im with an added IMObject 
         *                      represented by the /ProperIMElement/ 
         *                      child of el
         *  else ->
         *      throw a ContactXMLError
         */
        addIMsFromElement(contact, el);

        /* - - - 7
         * return contact
         */
        return contact;
    }


    /* - - - a d d N a m e s F r o m E l e m e n t 
     * let
     *  /ProperNameElement/ = an XML element with the tag name 
     *                          ContactName and with child elements 
     *                          with tag names FirstName, MiddleName,
     *                          SirName, and NickName
     * in ->
     *  if el has a /ProperNameElement/ child ->
     *      contact.name := contact.name with an added NameObject 
     *                      represented by the /ProperNameElement/ 
     *                      child of el
     *  else ->
     *      throw a ContactXMLError
     */
    private void addNamesFromElement(ContactObject contact, Element el) 
        throws ContactXMLError
    {
        /* - - - 1
         * name_list := a NodeList child elements of el with the tag name
         *              ContactName
         */
        NodeList name_list = el.getElementsByTagName("ContactName");

        /* - - - 2
         * if name_list is empty ->
         *  throw ContactXMLError
         * else ->
         *  contact.name := contact.name with an added NameObject 
         *                  represented by the /ProperNameElement/ 
         *                  child of el
         */
        
        /* - - 2.1
         * throw ContactXMLError with a message regarding ContactName
         */
        if (name_list == null || name_list.getLength() == 0) 
            throw new ContactXMLError("ContactName");
        /* - - 2.2
         *  contact.name := contact.name with an added NameObject 
         *                  represented by the /ProperNameElement/ 
         *                  child of el
         */                  
        else
        { 
            /* - 2.2.1
             * name := new instance of NameObject
             */
            NameObject name = new NameObject();

            /* - 2.2.2
             * let /FirstNameElement/ = an XML element with the tag name
             *                          FirstName and is a child element
             *                          of the current ContactName element
             * in ->
             *  if el has a /FirstNameElement/ ->
             *      name.firstName := the text value of /FirstNameElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding FirstName
             */
            addFirstNameFromElement(name, el);

            /* - 2.2.3
             * let /MiddleNameElement/ = an XML element with the tag name
             *                            MiddleName and is a child element
             *                            of the current ContactName element
             * in ->
             *  if el has a /MiddleNameElement/
             *      name.middleName := the text value of /MiddleNameElement/
             *  else
             *      throw a ContactXMLError with a message regarding MiddleName
             */
            addMiddleNameFromElement(name, el);

            /* - 2.2.4
             * let /SirNameElement/ = an XML element with the tag name
             *                        SirName and is a child element
             *                        of the current ContactName element
             * in ->
             *  if el has a /SirNameElement/
             *      name.middleName := the text value of /SirNameElement/
             *  else
             *      throw a ContactXMLError with a message regarding SirName
             */
            addSirNameFromElement(name, el);

            /* - 2.2.5
             * let /NickNameElement/ = an XML element with the tag name
             *                         NickName and is a child element
             *                         of the current ContactName element
             * in ->
             *  if el has a /NickNameElement/
             *      name.SirName := the text value of /SirNameElement/
             *  else
             *      throw a ContactXMLError with a message regarding SirName
             */
            addNickNameFromElement(name, el);

            /* - 2.2.6 
             * contact.name := contact.name with an added NameObject 
             *                 represented by the /ProperNameElement/ 
             *                 child of el
             */
            contact.addName(name);
        }
    }

    /* - - -a d d F i r s t N a m e F r o m E l e m e n t
     * let /FirstNameElement/ = an XML element with the tag name
     *                          FirstName and is a child element
     *                          of the current ContactName element
     * in ->
     *  if el has a /FirstNameElement/ ->
     *      name.firstName := the text value of /FirstNameElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding FirstName
     */ 
    private void addFirstNameFromElement(NameObject name, Element el)
        throws ContactXMLError
    {
        
        NodeList fname_l = el.getElementsByTagName("FirstName");
        if (fname_l == null || fname_l.getLength() == 0)
            throw new ContactXMLError("FirstName");
        else
        {
            String fname = "";
            Element fname_el = (Element)fname_l.item(0);
            if (fname_el.getFirstChild() != null);
                fname = fname_el.getFirstChild().getNodeValue();
            name.firstName = fname;
        }
    }

    /* - - - a d d M i d d l e N a me F r o m E l e m e n t
     * let /MiddleNameElement/ = an XML element with the tag name
     *                            MiddleName and is a child element
     *                            of the current ContactName element
     * in ->
     *  if el has a /MiddleNameElement/
     *      name.middleName := the text value of /MiddleNameElement/
     *  else
     *      throw a ContactXMLError with a message regarding MiddleName
     */
    private void addMiddleNameFromElement(NameObject name, Element el)
        throws ContactXMLError
    {
        NodeList mname_l = el.getElementsByTagName("MiddleName");
        if (mname_l == null || mname_l.getLength() == 0)
            throw new ContactXMLError("MiddletName");
        else
        {
            String mname = "";
            Element mname_el = (Element)mname_l.item(0);
            if (mname_el.getFirstChild() != null)
                mname = mname_el.getFirstChild().getNodeValue();
            name.middleName = mname;
        }
    }

    /* - - - a d d S i r N a m e F r o m E l e m e n t
     * let /SirNameElement/ = an XML element with the tag name
     *                        SirName and is a child element
     *                        of the current ContactName element
     * in ->
     *  if el has a /SirNameElement/
     *      name.middleName := the text value of /SirNameElement/
     *  else
     *      throw a ContactXMLError with a message regarding SirName
     */
    private void addSirNameFromElement(NameObject name, Element el)
        throws ContactXMLError
    {
        NodeList sname_l = el.getElementsByTagName("SirName");
        if (sname_l == null || sname_l.getLength() == 0)
            throw new ContactXMLError("SirName");
        else
        {
            String sname = "";
            Element sname_el = (Element)sname_l.item(0);
            if (sname_el.getFirstChild() != null)
                sname = sname_el.getFirstChild().getNodeValue();
            name.sirName = sname;
        }
    }

    /* - - - a d d N i c k N a m e F r o m E l e m e n t
     * let /NickNameElement/ = an XML element with the tag name
     *                         NickName and is a child element
     *                         of the current ContactName element
     * in ->
     *  if el has a /NickNameElement/
     *      name.SirName := the text value of /SirNameElement/
     *  else
     *      throw a ContactXMLError with a message regarding SirName
     */
    private void addNickNameFromElement(NameObject name, Element el)
        throws ContactXMLError
    {
        NodeList nname_l = el.getElementsByTagName("NickName");
        if (nname_l == null || nname_l.getLength() == 0)
            throw new ContactXMLError("NickName");
        else
        {
            String nname = "";
            Element nname_el = (Element)nname_l.item(0);
            if (nname_el.getFirstChild() != null)
                nname = nname_el.getFirstChild().getNodeValue();
            name.nickName = nname;
        }
    }
    

    /* - - - a d d P h o n e s F r o m E l e m e n t 
     * let
     *  /PropePhoneElement/ = an XML element with the tag name 
     *                          ContactPhone and with child elements 
     *                          with tag names AreaCode and LocalNumber
     * in ->
     *  if el has a /ProperPhoneElement/ child ->
     *      contact.phoneNumber := contact.phoneNumber with an added PhoneObject 
     *                      represented by the /ProperPhoneElement/ 
     *                      child of el
     *  else ->
     *      throw ContactXMLError
     */
    private void addPhonesFromElement(ContactObject contact, Element el)
        throws ContactXMLError
    {
        /* - - - 1
         * phone_list := a NodeList child elements of el with the tag name
         *              ContactPhone
         */
        NodeList phone_list = el.getElementsByTagName("ContactPhone");

        /* - - - 2
         * if phone_list is empty ->
         *  throw ContactXMLError with a message regarding ContactPhone
         * else ->
         *  contact.phoneNumber := contact.name with an added NameObject 
         *                         represented by the /ProperPhoneElement/
         *                         child of el
         */
        
        /* - - 2.1
         * throw ContactXMLError with a message regarding ContactPhone
         */
        if (phone_list == null || phone_list.getLength() == 0)
            throw new ContactXMLError("ContactPhone");
        /* - - 2.2
         *  contact.phoneNumber := contact.name with an added PhoneObject 
         *                         represented by the /ProperPhoneElement/ 
         *                         child of el
         */ 
        else
        {
            /* - 2.2.1
             * name := new instance of NameObject
             */
            PhoneObject phone = new PhoneObject();

            /* - 2.2.2
             * let /AreaCodeElement/ = an XML element with the tag name
             *                         AreaCode and is a child element
             *                         of the current ContactPhone element
             * in ->
             *  if el has a /AreaCodeElement/ ->
             *      phone.areaCode := the text value of /AreaCodeElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding AreaCode
             */
            addAreaCodeFromElement(phone, el);

            /* - 2.2.3
             * let /LocalNumberElement/ = an XML element with the tag name
             *                         LocalNumber and is a child element
             *                         of the current ContactPhone element
             * in ->
             *  if el has a /LocalNumberElement/ ->
             *      phone.localNumber := the text value of /LocalNumberElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding LocalNumber
             */
            addLocalNumberFromElement(phone, el);

            /* - 2.2.4
             * contact.phoneNumber := contact.name with an added NameObject 
             *                 represented by the /ProperPhoneElement/ 
             *                 child of el
             */
            contact.addPhoneNumber(phone);
        }
    }

    /* - - - a d d A r e a C o d e F r o m E l e m e n t
     * let /AreaCodeElement/ = an XML element with the tag name
     *                         AreaCode and is a child element
     *                         of the current ContactPhone element
     * in ->
     *  if el has a /AreaCodeElement/ ->
     *      phone.areaCode := the text value of /AreaCodeElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding AreaCode
     */
    private void addAreaCodeFromElement(PhoneObject phone, Element el)
        throws ContactXMLError
    {
        NodeList area_l = el.getElementsByTagName("AreaCode");
        if (area_l == null || area_l.getLength() == 0)
            throw new ContactXMLError("NickName");
        else
        {
            String area= "";
            Element area_el = (Element)area_l.item(0);
            if (area_el.getFirstChild() != null)
                area = area_el.getFirstChild().getNodeValue();
            phone.areaCode = area;
        }
    }
    
    /* - - - a d d L o c a l N u m b e r F r o m E l e m e n t
     * let /LocalNumberElement/ = an XML element with the tag name
     *                         LocalNumber and is a child element
     *                         of the current ContactPhone element
     * in ->
     *  if el has a /LocalNumberElement/ ->
     *      phone.localNumber := the text value of /LocalNumberElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding LocalNumber
     */
    private void addLocalNumberFromElement(PhoneObject phone, Element el)
        throws ContactXMLError
    {
        NodeList local_l = el.getElementsByTagName("LocalNumber");
        if (local_l == null || local_l.getLength() == 0)
            throw new ContactXMLError("NickName");
        else
        {
            String local = "";
            Element local_el = (Element)local_l.item(0);
            if (local_el.getFirstChild() != null)
                local = local_el.getFirstChild().getNodeValue();
            phone.localNumber = local;
        }
    }


    /* - - - a d d A d d r e s s e s F r o m E l e m e n t
     * let
     *  /ProperAddressElement/ = an XML element with the tag name 
     *                          ContactAddress and with child elements 
     *                          with tag names State, City,
     *                          Zip, and StreetAddress
     * in ->
     *  if el has a /ProperAddressElement/ ->
     *      contact.address := contact.address with an added AddressObject 
     *                      represented by the /ProperAddressElement/ 
     *                      child of el
     *  else ->
     *      throw ContactXMLError
     */
    private void addAddressesFromElement(ContactObject contact, Element el)
        throws ContactXMLError
    {
        /* - - - 1
         * addr_list := a NodeList child elements of el with the tag name
         *              ContactAddress
         */
        NodeList addr_list = el.getElementsByTagName("ContactAddress");

        /* - - - 2
         * if addr_list is empty ->
         *  throw ContactXMLError with a message regarding ContactAddress
         * else ->
         *  contact.phoneNumber := contact.name with an added NameObject 
         *                         represented by the /ProperAddressElement/
         *                         child of el
         */

        /* - - 2.1
         * throw ContactXMLError with a message regarding ContactAddress
         */
        if (addr_list == null || addr_list.getLength() == 0)
            throw new ContactXMLError("ContactAddress");
        /* - - 2.2
         *  contact.address := contact.name with an added AddressObject
         *                     represented by the /ProperAddressElement/ 
         *                     child of el
         */
        else
        {
            /* - 2.2.1
             * addr := new instance of AddressObject
             */
            AddressObject addr = new AddressObject();

            /* - 2.2.2
             * let /StateElement/ = an XML element with the tag name
             *                      State and is a child element
             *                      of the current ContactAddress element
             * in ->
             *  if el has a /StateElement/ ->
             *      addr.state := the text value of /StateElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding State
             */
            addStateFromElement(addr, el);

            /* - 2.2.3
             * let /CityElement/ = an XML element with the tag name
             *                     City and is a child element
             *                     of the current ContactAddress element
             * in ->
             *  if el has a /CityElement/ ->
             *      addr.city := the text value of /CityElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding City
             */
            addCityFromElement(addr, el);

            /* - 2.2.4
             * let /ZipElement/ = an XML element with the tag name
             *                    ZipCode and is a child element
             *                    of the current ContactAddress element
             * in ->
             *  if el has a /ZipElement/ ->
             *      addr.zipCode := the text value of /ZipElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding ZipCode
             */
            addZipFromElement(addr, el);

            /* - 2.2.5
             * let /StreetElement/ = an XML element with the tag name
             *                    StreetCode and is a child element
             *                    of the current ContactAddress element
             * in ->
             *  if el has a /StreetElement/ ->
             *      addr.streetAddress := the text value of /StreetElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding StreetAddress
             */
            addStreetFromElement(addr, el);

            /* - 2.2.6
             * contact.address := contact.name with an added AddressObject 
             *                 represented by the /PropeAddressElement/ 
             *                 child of el
             */
            contact.addAddress(addr);
        }
    }

    /* - - - a d d S t a t e F r o m E l e m e n t
     * let /StateElement/ = an XML element with the tag name
     *                      State and is a child element
     *                      of the current ContactAddress element
     * in ->
     *  if el has a /StateElement/ ->
     *      addr.state := the text value of /StateElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding State
     */
    private void addStateFromElement(AddressObject addr, Element el)
        throws ContactXMLError
    {
        NodeList state_l = el.getElementsByTagName("State");
        if (state_l == null || state_l.getLength() == 0)
            throw new ContactXMLError("State");
        else
        {
            String state = "";
            Element state_el = (Element)state_l.item(0);
            if (state_el.getFirstChild() != null)
                state = state_el.getFirstChild().getNodeValue();
            addr.state = state;
        }
    }

    /* - - - a d d C i t y F r o m E l e m e n t
     * let /CityElement/ = an XML element with the tag name
     *                     City and is a child element
     *                     of the current ContactAddress element
     * in ->
     *  if el has a /CityElement/ ->
     *      addr.city := the text value of /CityElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding City
     */
    private void addCityFromElement(AddressObject addr, Element el)
        throws ContactXMLError
    {
        NodeList city_l = el.getElementsByTagName("City");
        if (city_l == null || city_l.getLength() == 0)
            throw new ContactXMLError("City");
        else
        {
            String city = "";
            Element city_el = (Element)city_l.item(0);
            if (city_el.getFirstChild() != null)
                city = city_el.getFirstChild().getNodeValue();
            addr.city = city;
        }
    }

    /* - - - a d d Z i p F r o m E l e m e n t
     * let /ZipElement/ = an XML element with the tag name
     *                    ZipCode and is a child element
     *                    of the current ContactAddress element
     * in ->
     *  if el has a /ZipElement/ ->
     *      addr.zipCode := the text value of /ZipElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding ZipCode
     */
    private void addZipFromElement(AddressObject addr, Element el)
        throws ContactXMLError
    {
        NodeList zip_l = el.getElementsByTagName("Zip");
        if (zip_l == null || zip_l.getLength() == 0)
            throw new ContactXMLError("Zip");
        else
        {
            String zip = "";
            Element zip_el = (Element)zip_l.item(0);
            if (zip_el.getFirstChild() != null)
                zip = zip_el.getFirstChild().getNodeValue();
            addr.zipCode = zip;
        }
    }
    
    /* - - - a d d S t r e e t F r o m E l e m e n t
     * let /StreetElement/ = an XML element with the tag name
     *                    StreetCode and is a child element
     *                    of the current ContactAddress element
     * in ->
     *  if el has a /StreetElement/ ->
     *      addr.streetAddress := the text value of /StreetElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding StreetAddress
     */
    private void addStreetFromElement(AddressObject addr, Element el)
        throws ContactXMLError
    {
        NodeList street_l = el.getElementsByTagName("StreetAddress");
        if (street_l == null || street_l.getLength() == 0)
            throw new ContactXMLError("StreetAddress");
        else
        {
            String street = "";
            Element street_el = (Element)street_l.item(0);
            if (street_el.getFirstChild() != null)
                street = street_el.getFirstChild().getNodeValue();
            addr.streetAddress = street;
        }
    }


    /* - - - a d d E m a i l s F r o m E l e m e n t
     * let
     *  /ProperEmailElement/ = an XML element with the tag name 
     *                          ContactEmail
     * in ->
     *  if el has a /ProperEmailElement/ child ->
     *      contact.email := contact.email with an added EmailObject 
     *                      represented by the /ProperEmailElement/ 
     *                      child of el
     *  else ->
     *      throw ContactXMLError
     */
    private void addEmailsFromElement(ContactObject contact, Element el)
        throws ContactXMLError
    {
        EmailObject email = new EmailObject();
        NodeList email_l = el.getElementsByTagName("Email");
        if (email_l == null || email_l.getLength() == 0)
            throw new ContactXMLError("Email");
        else
        {
            String email_s = "";
            Element email_el = (Element)email_l.item(0);
            if (email_el.getFirstChild() != null)
                email_s = email_el.getFirstChild().getNodeValue();
            email.emailAddress = email_s;
            contact.addEmail(email);
        }
    }


    /* - - - a d d I M s F r o m E l e m e n t
     * let
     *  /ProperIMElement/ = an XML element with the tag name 
     *                      ContactIM and with child elements 
     *                      with tag names Provider and UserName
     * in ->
     *  if el has a /ProperIMElement/ child->
     *      contact.im := contact.im with an added IMObject 
     *                      represented by the /ProperIMElement/ 
     *                      child of el
     *  else ->
     *      throw ContactXMLError
     */
    private void addIMsFromElement(ContactObject contact, Element el)
        throws ContactXMLError
    {
        /* - - - 1
         * im_list := a NodeList child elements of el with the tag name
         *              ContactIM
         */
        NodeList im_list = el.getElementsByTagName("ContactIM");

        /* - - - 2
         * if im_list is empty ->
         *  throw ContactXMLError with a message regarding ContactIM
         * else ->
         *  contact.im := contact.name with an added NameObject 
         *                         represented by the /ProperIMElement/
         *                         child of el
         */

        /* - - 2.1
         * throw ContactXMLError with a message regarding ContactAddress
         */
        if (im_list == null || im_list.getLength() == 0)
            throw new ContactXMLError("ContactIM");
         /* - - 2.2
         *  contact.im := contact.im with an added AddressObject
         *                represented by the /ProperAddressElement/ 
         *                child of el
         */
        else
        {
            /* - 2.2.1
             * im := new instance of IMObject
             */
            IMObject im = new IMObject();
            
            /* - 2.2.2
             * let /ProviderElement/ = an XML element with the tag name
             *                       Provider and is a child element
             *                       of the current ContactIM element
             * in ->
             *  if el has a /ProviderElement/ ->
             *      im.provider := the text value of /ProviderElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding Provider
             */
            addProviderFromElement(im, el);

            /* - 2.2.3
             * let /UserNameElement/ = an XML element with the tag name
             *                       UserName and is a child element
             *                       of the current ContactIM element
             * in ->I
             *  if el has a /UserNameElement/ ->
             *      im.provider := the text value of /UserNameElement/
             *  else ->
             *      throw a ContactXMLError with a message regarding UserName
             */
            addUserNameFromElement(im, el);

            /* - 2.2.6
             * contact.im := contact.name with an added IMObject 
             *                 represented by the /ProperIMElement/ 
             *                 child of el
             */
            contact.addIM(im);
        }
    }

    /* - - - a d d P r o v i d e r F r o m E l e m e n t
     * let /ProviderElement/ = an XML element with the tag name
     *                       Provider and is a child element
     *                       of the current ContactIM element
     * in ->
     *  if el has a /ProviderElement/ ->
     *      im.provider := the text value of /ProviderElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding Provider
     */
    private void addProviderFromElement(IMObject im, Element el)
        throws ContactXMLError
    {
        NodeList prov_l = el.getElementsByTagName("Provider");
        if (prov_l == null || prov_l.getLength() == 0)
            throw new ContactXMLError("Provider");
        else
        {
            String prov = "";
            Element prov_el = (Element)prov_l.item(0);
            if (prov_el.getFirstChild() != null)
                prov = prov_el.getFirstChild().getNodeValue();
            im.provider = prov;
        }
    }

    /* - - - a d d P r o v i d e r F r o m E l e m e n t
     * let /UserNameElement/ = an XML element with the tag name
     *                       UserName and is a child element
     *                       of the current ContactIM element
     * in ->I
     *  if el has a /UserNameElement/ ->
     *      im.provider := the text value of /UserNameElement/
     *  else ->
     *      throw a ContactXMLError with a message regarding UserName
     */
    private void addUserNameFromElement(IMObject im, Element el)
        throws ContactXMLError
    {
        NodeList user_l = el.getElementsByTagName("UserName");
        if (user_l == null || user_l.getLength() == 0)
            throw new ContactXMLError("UserName");
        else
        {
            String user = "";
            Element user_el = (Element)user_l.item(0);
            if (user_el.getFirstChild() != null)
                user = user_el.getFirstChild().getNodeValue();
            im.userName = user;
        }
    }
}
